package de.justphil.tcg.tcgserver.websockets.services;

import static de.justphil.tcg.tcgserver.commons.logging.TCGLogging.*;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import org.slf4j.Logger;


import de.justphil.tcg.tcgserver.websockets.config.WebSocketConfig;

public class ConnectionCloseHandler {
	private static final Logger log = logger(ConnectionCloseHandler.class);
	
	private final ScheduledExecutorService exec;
	private final ConcurrentMap<Long, TimeOutTask> scheduledTimeOutTasks;
	private volatile OnReconnectTimeOutListener rtoListener;
	
	
	
	public ConnectionCloseHandler() {
		exec = Executors.newScheduledThreadPool(WebSocketConfig.CCH_THREAD_POOL_SIZE);
		scheduledTimeOutTasks = new ConcurrentHashMap<Long, TimeOutTask>();
	}
	
	/* ############################################################################################ */
	/* ############################################################################################ */
	/* ############################################################################################ */
	
	public void onClose(long gameId, long playerId) {
		log.info("Player '{}' has ungracefully closed the connection. [onClose()] -> has {} {} to reconnect!", new Object[] {
				playerId, WebSocketConfig.RECONNECT_TIMEOUT, WebSocketConfig.RECONNECT_TIMEOUT_UNIT
		});
		
		ScheduledFuture<?> sf = exec.schedule(
				createTimeOutTask(gameId, playerId), WebSocketConfig.RECONNECT_TIMEOUT, WebSocketConfig.RECONNECT_TIMEOUT_UNIT
		);
		scheduledTimeOutTasks.put(playerId, new TimeOutTask(sf, gameId));
	}
	
	public void onReconnect(long playerId) {
		log.info("Player '{}' onReconnect()", playerId);
		
		TimeOutTask tot = scheduledTimeOutTasks.remove(playerId);
		if (tot != null) {
			ScheduledFuture<?> sf = tot.getScheduledFuture();
			
			if (sf.cancel(true)) {
				log.debug("TimeOutTask for player '{}' is canceled.", playerId);
			}
			else {
				log.debug("TimeOutTask for player '{}' cannot be canceled.", playerId);
			}
		}
		else {
			log.debug("TimeOutTask for player '{}' does not exist.", playerId);
		}
	}
	
	public void cleanUp(long playerId) {
		onReconnect(playerId);
	}
	
	public void setOnReconnectTimeOutListener(OnReconnectTimeOutListener rtoListener) {
		this.rtoListener = rtoListener;
	}
	
	public void removeOnReconnectTimeOutListener() {
		this.rtoListener = null;
	}
	
	/* ############################################################################################ */
	/* ############################################################################################ */
	/* ############################################################################################ */
	
	public interface OnReconnectTimeOutListener {
		public void onReconnectTimeOut(long gameId, long playerId);
	}
	
	/* ############################################################################################ */
	/* ############################################################################################ */
	/* ############################################################################################ */
	
	private void notifyListenerAboutTimeOut(long gameId, long playerId) {
		if (rtoListener != null)
			rtoListener.onReconnectTimeOut(gameId, playerId);
	}
	
	private Runnable createTimeOutTask(long gameId, long playerId) {
		final long gId = gameId;
		final long pId = playerId;
		
		return new Runnable() {

			@Override
			public void run() {
				scheduledTimeOutTasks.remove(pId);
				notifyListenerAboutTimeOut(gId, pId);
			}
			
		};
	}
}
